Skip to main content

JS Notes 2

Javascript: Class Inheritance, promise and callback

Class

Class Inheritance, static properties and methods.

Helpful Link: javascript.info

Class inheritance in js inherits protoype methods and properties from parents, unless child's property have the same key.


class Animal{
constructor(name, weight){
this.name = name;
this.speed = 0;
}
run(speed){
this.speed = speed;
console.log(`Running at speed of ${speed}`);
}
}

class Rabbit extends Animal{
constructor(name, ...args){
// Derived constructor must call super to init the creation and assignment of object and this.
// A default constructor will be defined automatically if derived constructor is not defined.
super(name);
[this.color, this.weight] = args;
}
hide(){
console.log(`${this.color} ${this.name} can hide, weights: ${this.weight}`);
}
}

let rabbit = new Rabbit("Rabbit", "White", 123);
rabbit.run(100);
rabbit.hide();

[[HomeObject]] and super

JS info

Summary

  • super.method() can't be implemented using this.__proto__.method.call(this).
  • That works in simple cases but breaks in inheritance chains, because this always refers to the current object, not the method’s home object.
  • The result: infinite recursion when trying to go further up the chain.

Solution is to use [[HomeObject]], this hidden property remebers the method was created in which allows super to find the correct parent avoiding recursion.

Extended clock example question from javascript.info
class Clock {
constructor({ template }) {
this.template = template;
console.log(this.template);
}

render() {
let date = new Date();

let hours = date.getHours();
if (hours < 10) hours = '0' + hours;

let mins = date.getMinutes();
if (mins < 10) mins = '0' + mins;

let secs = date.getSeconds();
if (secs < 10) secs = '0' + secs;

let output = this.template
.replace('h', hours)
.replace('m', mins)
.replace('s', secs);

console.log(output);
}

stop() {
clearInterval(this.timer);
}

start() {
this.render();
this.timer = setInterval(() => this.render(), 1000);
}
}

class ExtendedClock extends Clock{
constructor(options){
super(options);
const {precision = 1000} = options;
this.precision = precision;
}

start() {
this.render();
this.timer = setInterval(() => this.render(), this.precision);
}
}

let a = new ExtendedClock({template: "h-m-s"});

a.start();
setTimeout(() => a.stop(), 5000);

Static property and methods

Static methods belongs to the class, shared for all instances. You can access using the class name not object.

class Test{
static name = "class name"
}

let a = new Test();

console.log(Test.name); // -> class name
console.log(a.name) // -> undefined

Inherit static methods

class Person{
static name = "class name"

constructor(){

}
}

class Student extends Person{
static cmp_gpa(student_a, student_b){
return student_a.gpa > student_b.gpa;
}

constructor(gpa){
super();
this.gpa = gpa;
}
}

let a = new Student(2.12);
let b = new Student(3.12);

console.log(`Student A has ${Student.cmp_gpa(a, b) ? "higher" : "lower"} GPA than student B.`);

Public, private, protected

Public - default

Protected properties - starts with underscore _, not enforced on language level just convention like in python. Use getter/setter with these properties. Is inherited.

read only - this._name would make property only changable during creation.

Private - #private_var, only accessable within can't be inherted. Doesn't conflict with public properties.

class Product {
#serial; // private field

constructor(name, price) {
this.name = name;
this.price = price; // go through the setter for validation
this.#serial = Math.floor(Math.random() * 1e8); // auto-generate serial
}

// getter and setter for price
get price() {
return this._price;
}

set price(newPrice) {
if (typeof newPrice !== "number" || newPrice < 0) {
throw new Error("Price must be a non-negative number.");
}
this._price = newPrice;
}

// getter for serial (read-only: no setter)
get serial() {
return this.#serial;
}

// a method for product info
info() {
return `Product: ${this.name}, Price: $${this.price}, Serial: ${this.serial}`;
}
}

// Example usage:
let a = new Product("My Product", 123);
console.log(a.info()); // ✅ shows product details
a.price = 200; // ✅ updates price safely
console.log(a.info());
console.log(a.serial);

Extending built-in classes

JS info

Promises, async/await

Callback

A callback is just a function passed as an argument to another function, usually to run later (often after an async operation like reading a file, fetching data, etc.).

function getData(callback) {
setTimeout(() => {
callback("Here is your data!");
}, 1000); // simulate 1s delay
}

getData((result) => {
console.log(result); // runs after 1s
});
danger

Problem: When you need multiple async steps, you end up with callback hell:

getData((result) => {
console.log(result);
getData((result2) => {
console.log(result2);
getData((result3) => {
console.log(result3);
// ... and so on
});
});
});

Promises

A Promise is an object that represents the eventual result of an async operation. It has two states: fulfilled (success) or rejected (error).


Fetch Example
let start = performance.now();

let data = fetch("https://microsoftedge.github.io/Demos/json-dummy-data/1MB.json").then(response => response.json()).catch(err => {
console.log("Error: " + err);
}).finally(() => {
console.log("Finished Processing. " + (performance.now() - start));
});

const response = await data;
console.log(Object.keys(response).length);

Async / Await